#include "usart.h"
#include "module_ymodem.h"
#include "driver_tool.h"
#include "driver_delay.h"
#include "driver_usart.h"

#define y_debug_out GUA_LOGE


/*******************************************
* @function: 	Ymodel协议的串口发送函数 ，方便修改串口发送函数内部流程，方便代码移植
* @input:		无
* @return:		无
* @author:		田小花
* @time:		2021.4.30
*******************************************/
void Ymodel_usart_sand_instruct(unsigned char Ymodel_instruct)//发送指令
{
	HAL_UART_Transmit_DMA(&huart1, &Ymodel_instruct, 1);
}


/*******************************************
* @function: 	Ymodel协议的串口接收函数，简单处理串口中断接收到的数据，通过改变串口接收函数内的代码方便移植
* @input:			无
* @return:		作为是否有接收到数据的判断标志
* @author:		田小花
* @time:			2021.4.30
*******************************************/
unsigned char Ymodel_usart_receive_instruct(unsigned char *buffer)//接收指令
{
	int i = 0;
	if(Rx_Flag)    	// 通过串口中断函数检测到了串口有数据保存
	{
		Rx_Flag=0;	// 清除串口中断的标志

		for(i=0; i<Rx_Len; i++)
		{
			buffer[i] = Rx_Buf[i];
		}
		return 1; //有接收到了数据
	}
	return 0;//没有接收到数据
}



/*******************************************
* @function: 	执行Ymodel协议的运行主要流程 ，同时进行数据处理，边接收数据边处理数据
* @input:			无
* @return:		无
* @author:		田小花
* @time:			2021.4.30
*******************************************/
unsigned char Ymodel_FLAG = 0; //作为运行流程的状态机标志位

uint8 uasrt_buffer[200] = {0}; //转存串口中断函数的数据

unsigned char FILE_name[50] = {0}; //保存接收文件的文件名字符串
unsigned char FILE_length[50] = {0}; //保存接收文件的文件长度字符串


unsigned char buffer[4096] = {0}; //需要做处理的数据

long int buffer_sign = 0;

int buffer_address = 0x000000;

void Ymodel_process_fun(void)
{
	int j = 0, i = 0;

	while(1)
	{
		switch(Ymodel_FLAG)//运行流程依靠的状态机
		{
			//数据起始帧
			case 0:
			{

				if(Ymodel_usart_receive_instruct(uasrt_buffer) == 1)//判断串口接收中断是否接收到了数据
				{
					if(uasrt_buffer[0] == 0x01) { //判断接收到的数据第一个数据是不是SOH SOH代表了128位数据

						//处理第一帧数据获取文件名
						for(j=3; j<Rx_Len; j++) //第一帧的第三个数据开始时文件的名字
						{
							if(uasrt_buffer[j] == 0x00) break; //文件名字以0x00数据结束

							FILE_name[j-3] = uasrt_buffer[j]; //把文件名字保存到数组内
						}
						j=j+1;
						i=0;
						//处理第一帧数获取文件长度
						for(; j<Rx_Len; j++)
						{
							if(uasrt_buffer[j] == 0x20) break;

							FILE_length[i++] = uasrt_buffer[j];
						}
						Ymodel_FLAG = 1; //跳转到标志位1

						break;
					}
				}

				HAL_Delay(200);

				Ymodel_usart_sand_instruct(C1);//发送应答

				break;
			}

			//发送ACK 和C
			case 1:
			{
				HAL_Delay(1);
				Ymodel_usart_sand_instruct(ACK);
				HAL_Delay(1);
				Ymodel_usart_sand_instruct(C1);
				HAL_Delay(10);

				Ymodel_FLAG = 2;

				break;
			}

			//接收数据帧
			case 2:
			{
				if(Ymodel_usart_receive_instruct(uasrt_buffer) == 1)//判断串口接收中断是否接收到了数据
				{
					//处理接收到的128字节数据长度的数据
					if(uasrt_buffer[0] == 0x01)//判断数据帧的第一个标志位是不是SOH
					{
						Ymodel_FLAG = 3; //跳转到发送应答数据帧

						for(j=0; j < 128; j++) //第4字节到第131字节：数据段内容 数据长度128字节
						{
							buffer[j+buffer_sign] = uasrt_buffer[j+3];//截取帧数据的数据内容
						}
						buffer_sign = buffer_sign+128;//每次接收128字节的数据

						//已经接收到了4K字节的数据，可以一次性将数据写入到W25Q64中去
						//不满4096个字节的数据是最后一帧数据，放在结束帧处理
						if(buffer_sign == 4096)
						{
							// WriteData_4096(buffer_address,buffer);//将读取到的数据写入存储器

							buffer_address = buffer_address + 0x001000; //每次写入4096个字节，也就是0x1000个字节的数据

							buffer_sign = 0;

							for(j=0; j < 4096; j++) buffer[j] = 0xFF; //清理接收的数据内容
						}

						break;//case结束
					}

					//处理接收到的1024字节数据长度的数据
					if(uasrt_buffer[0] == 0x02)
					{
						Ymodel_FLAG = 0; //这里不做数据处理，不支持
					}

					//处理接收结束数据帧
					if(uasrt_buffer[0] == 0x04) //EOT 0x04结束数据帧
					{
						Ymodel_FLAG = 4; //跳转至发送应答结束数据帧的数据NAK

						//在传输最后一帧数据的时候一定不够4096个字节
						//所以将最后一帧不够4096字节的数据单独写入
						// WriteData_4096(buffer_address,buffer);//将读取到的数据写入存储器
					}


					//检测到主机取消了发送
					if(uasrt_buffer[0] == 0x18)
					{
						printf("Undo Send\r\n");
						Ymodel_FLAG = 0;
					}
				}
				HAL_Delay(1);
				break;
			}

			//发送ACK应答
			case 3:
			{
				HAL_Delay(1);
				Ymodel_usart_sand_instruct(ACK);
				HAL_Delay(1);
				Ymodel_FLAG = 2;
				break;
			}

			//检测到了数据结束帧 EOT 需要使用NAK回应ETO请求结束传输
			case 4:
			{
				Ymodel_FLAG = 5; //跳转到下一个状态

				HAL_Delay(10);

				Ymodel_usart_sand_instruct(NAK);//发送NAK数据帧 表示结束数据传输

				HAL_Delay(10);

				break;
			}

			//判断进入传输数据结束流程
			case 5:
			{
				if(Ymodel_usart_receive_instruct(uasrt_buffer) == 1)//判断串口接收中断是否接收到了数据
				{
					//检测到主机取消了发送
					if(Rx_Buf[0] == 0x04)
					{
						Ymodel_FLAG = 6;
					}
					break;
				}
				break;
			}


			//数据传输结束前需要再次发送应答数据帧和回应帧，通知发送端结束数据传输
			case 6:
			{
				Ymodel_FLAG = 7;

				HAL_Delay(2);
				Ymodel_usart_sand_instruct(ACK);
				HAL_Delay(1);
				Ymodel_usart_sand_instruct(C1);
				HAL_Delay(1);

				break;
			}

			//通过判断最后一个数据帧判断通讯已经完全结束
			case 7:
			{
				if(Ymodel_usart_receive_instruct(uasrt_buffer) == 1)//判断串口接收中断是否接收到了数据
				{
					//检测到主机取消了发送
					if(Rx_Buf[0] == 0x01)
					{
						Ymodel_FLAG = 8;
					}
				}
				break;
			}

			//最后数据传输结束，输出打印信息
			case 8:
			{
				Ymodel_usart_sand_instruct(ACK);

				HAL_Delay(10);

				//显示已经传输完成
				printf("\r\nData transmission completed\r\n");

				//显示接收到的文件的名字
				printf("FILE_name :");

				for(j=0; j<sizeof(FILE_name)/sizeof(FILE_name[0]); j++)
				{
					printf("%c",FILE_name[j]);
				}
				printf("\r\n");

				//显示接收到的数据长度
				printf("FILE_length :");

				for(j=0; j<sizeof(FILE_length)/sizeof(FILE_length[0]); j++)
				{
					printf("%c",FILE_length[j]);
				}
				printf("Byts\r\n");

	//			//显示最后一帧接收到的数据内容
	//			printf("end data %d \r\n", sizeof(buffer)/sizeof(buffer[0]) );
	//			/* 输出数组内的信息 */
	//			for(j=0;j<4096;j++)
	//			{
	//				printf("%c ",buffer[j]);
	//
	//				if((j+1)%8 == 0) printf ("   ");
	//
	//				if((j+1)%64 == 0) printf ("\r\n");
	//
	//				if((j+1)%256 == 0) printf ("%d字节->\r\n\r\n",j+1);
	//			}

				printf ("\r\n\r\n显示内存数据\r\n\r\n");

				for(j=0; j<0x003000; j=j+0x001000)//循环显示12K字节数据内容
				{
					// ReadData_4096(j);

					/* 显示存储器内的数据 */
					for(i=0;i<4096;i++)
					{
						// printf("%c ",rdata[i]);

						if((i+1)%8 == 0) printf ("   ");

						if((i+1)%64 == 0) printf ("\r\n");

						if((i+1)%256 == 0) printf ("%d字节->\r\n\r\n",i+1);
					}
					printf ("\r\n------------------------------------------------------------------------------\r\n\r\n");
				}


				Ymodel_FLAG = 0; //回到初始状态，接收数据

				buffer_sign = 0; //归位数组的位置

				for(j=0; j<50; j++) //清除文件名字和文件长度的内容
				{
					FILE_name[j] = 0;
					FILE_name[j] = 0;
				}
				break;
			}

			case 10:
			{
				//处理第一帧数获取文件长度
				printf("\r\n");

				for(j=0; j<30; j++)
				{
					printf("%x ",uasrt_buffer[j]);
				}

				printf("\r\n");

				for(j=0; j<30; j++)
				{
					printf("%x ",FILE_length[j]);
				}

				while(1);
			}
		}
	}
}


#define YMODEM_FRAME_LEN     128
#define YMODEM_RECV_TIMEOUT  1000
#define YMODEM_ORDER_STEUP   "rb -E"
#define YMODEM_ORDER_C1      0x43
#define YMODEM_ORDER_ACK     0x06
#define YMODEM_ORDER_EOT     0x04
#define	YMODEM_ORDER_SOH	0x01  // 128字节数据包
#define	YMODEM_ORDER_STX	0x02  // 1024字节数据包
#define	YMODEM_ORDER_NAK	0x15

#define	YMODEM_ORDER_CAN	0x18
#define STX	0x02



typedef enum {
    YMODEM_BEGIN = 0,
    YMODEM_SETUP,
	YMODEM_RECV_DATA,
	YMODEM_RECV_END,
	YMODEM_RECV_DISCONNECT,
} YMODEM_STEP;

#define YMODEM_DATA_LEN_128 128
#define YMODEM_DATA_LEN_1024 1024

typedef struct
{
	uint16 ymodel_status;
	uint8 recv_buf[Rx_Max];
	uint8 filename[64];
	uint32 ymodem_data_len;
	uint8 tar_header;
	uint8 buffer_page[256];
	int32 seek_address;
} YMODEM_CTL;

static YMODEM_CTL g_ymodem_ctl = {0};
uint8 test_recv_buf[Rx_Max];
uint32 test_recv_len;

//直接计算法计算crc
uint16 do_crc(uint8 *ptr, int len)
{
    uint32 i;
    uint16 crc = 0x0000;

    while(len--)
    {
        crc ^= (uint16)(*ptr++) << 8;
        for (i = 0; i < 8; ++i)
        {
            if (crc & 0x8000)
                crc = (crc << 1) ^ 0x1021;
            else
                crc <<= 1;
        }
    }

    return crc;
}



static int32 recv_usart_data(uint8 *buffer, uint32 buffer_len)
{
	// memset(buffer, 0, buffer_len);

	if(Rx_Flag) {
		Rx_Flag=0;	// 清除串口中断的标志
		memcpy(buffer, Rx_Buf, Rx_Len);
		return REV_OK;
	}
	return REV_ERR;
}


static int32 send_usart_order(uint8 order)//发送指令
{
	return HAL_UART_Transmit_DMA(&huart1, &order, 1);
}


// https://blog.csdn.net/u012028275/article/details/112067463
// CRC16-XMODEM校验（x16+x12+x5+1）的高八位和低八位
static int32 ymodem_setup(uint8 *buffer, uint32 buffer_len, uint32 timeout)
{
	int32 i = 0;

	while (1) {
		if (recv_usart_data(buffer, buffer_len) == REV_OK) {
            if(strstr((char *)buffer, YMODEM_ORDER_STEUP) != NULL) {
                return REV_OK;
            } else {
                return REV_ERR;
            }
        }

		i++;
		if (i > timeout) {
			// y_debug_out("ymodem steup timeout!\r\n");
			return REV_ERR;
		}
        delay_ms(1);
	}
}


static int32 ymodem_recv_first_frame(uint8 *buffer, uint32 buffer_len, uint32 timeout)
{
	int32 i = 0;

	while (1) {
		if (recv_usart_data(buffer, buffer_len) == REV_OK) {
            return buffer[0];
        }

		i++;
		if (i > timeout) {
			y_debug_out("ymodem steup timeout!\r\n");
			return REV_ERR;
		}
        delay_ms(1);
	}
}


static int32 ymodem_dmessage_first_frame(uint8 *buffer, uint32 buffer_len, uint8 *file_name, uint32 tar_len)
{
//	int32 temp = 0;
	uint16 crc_rev = 0;

	crc_rev = do_crc(buffer+3, tar_len); // CRC检验只校验数据部分，即除去前三位
	if (((crc_rev >> 8) & 0xFF) != buffer[131] || (crc_rev & 0xFF) != buffer[132]) {
		y_debug_out("ymodem crc erroe! [0x%04x][0x%02x][%02x] [0x%02x][0x%02x]\r\n",
			crc_rev, ((crc_rev >> 8) & 0xFF), (crc_rev & 0xFF), buffer[131] , buffer[132]);
		y_debug_out("\r\n");
		for (int i=0; i<Rx_Max; i++) {
			y_debug_out("%02x ", buffer[i]);
			// y_debug_out("%d: 0x%02x\r\n", i, buffer[i]);
		}
		y_debug_out("\r\n");

		return REV_ERR;
	}

	strcpy((char *)file_name, (char *)buffer+3);

	return REV_OK;
}


static int32 ymodem_recv_data_frame(uint8 *buffer, uint32 buffer_len,  uint32 timeout, uint32 tar_len)
{
	int32 i = 0;
	uint16 crc_rev = 0;

	while (1) {
		if (recv_usart_data(buffer, buffer_len) == REV_OK) {
			crc_rev = do_crc(buffer+3, tar_len); // CRC检验只校验数据部分，即除去前三位
			if (((crc_rev >> 8) & 0xFF) != buffer[131] || (crc_rev & 0xFF) != buffer[132]) {
				y_debug_out("ymodem crc erroe! [0x%04x][0x%02x][%02x] [0x%02x][0x%02x]\r\n",
					crc_rev, ((crc_rev >> 8) & 0xFF), (crc_rev & 0xFF), buffer[131] , buffer[132]);
				y_debug_out("\r\n");
				for (int i=0; i<Rx_Max; i++) {
					y_debug_out("%02x ", buffer[i]);
					// y_debug_out("%d: 0x%02x\r\n", i, buffer[i]);
				}
				y_debug_out("\r\n");

				return REV_ERR;
			}


			return buffer[0];
        }

		i++;
		if (i > timeout) {
			y_debug_out("ymodem steup timeout!\r\n");
			return REV_ERR;
		}
        delay_ms(1);
	}
}





static int32 ymodem_recv_end_frame(uint8 *buffer, uint32 buffer_len, uint32 timeout, uint8 tar_header)
{
	int32 i = 0;

	while (1) {
		if (recv_usart_data(buffer, buffer_len) == REV_OK) {
            if(buffer[0] == tar_header) {
                return REV_OK;
            } else {
                return REV_ERR;
            }
        }

		i++;
		if (i > timeout) {
			y_debug_out("ymodem steup timeout!\r\n");
			return REV_ERR;
		}
        delay_ms(1);
	}
}


static int32 ymodem_disconnect(uint8 *buffer, uint32 buffer_len, uint32 timeout, uint8 tar_header)
{
	int32 i = 0;

	while (1) {
		if (recv_usart_data(buffer, buffer_len) == REV_OK) {
            if(buffer[0] == tar_header) {
                return REV_OK;
            } else {
				y_debug_out("ymodem error! ymodem_disconnect[0x%02x]!\r\n", buffer[0]);
                return REV_ERR;
            }
        }

		i++;
		if (i > timeout) {
			y_debug_out("ymodem steup timeout!\r\n");
			return REV_ERR;
		}
        delay_ms(1);
	}
}


static int32 strong_recv_data(storage_data_cb callback, uint32 start_address, uint8 *data, uint32 len)
{
	int32 rev = 0;

	if (callback == NULL) {
		y_debug_out("callback error!\r\n");
		while (1) { }
		return REV_ERR;
	}

	if (len != 128) {
		while (1) { }
		return REV_ERR;
	}

	memcpy(g_ymodem_ctl.buffer_page + g_ymodem_ctl.seek_address, data, len);

	g_ymodem_ctl.seek_address += len;
	if (g_ymodem_ctl.seek_address >= 0x100) {
		g_ymodem_ctl.seek_address = 0;

		rev = callback(start_address, g_ymodem_ctl.buffer_page, 0x100);
		if (rev != REV_OK) {
			y_debug_out("write data error!\r\n");
			while (1) { }
			return rev;
		}

		memset(g_ymodem_ctl.buffer_page, 0, 0x100);
	}

	return REV_OK;
}

// 参考：https://zhuanlan.zhihu.com/p/654782539
int32 ymodem_handel(storage_data_cb callback, uint32 start_address)
{
	int32 rev = 0;
	int32 flag = 0;

	while (1) {
		switch (g_ymodem_ctl.ymodel_status) {
			case YMODEM_BEGIN: {
					rev = ymodem_setup(g_ymodem_ctl.recv_buf, Rx_Max, YMODEM_RECV_TIMEOUT);
					if (rev == REV_OK) {
						g_ymodem_ctl.ymodel_status = YMODEM_SETUP;
					}

					send_usart_order(YMODEM_ORDER_C1);
					delay_ms(1);
					break;
				}


			case YMODEM_SETUP: {
					rev = ymodem_recv_first_frame(g_ymodem_ctl.recv_buf, Rx_Max, YMODEM_RECV_TIMEOUT);
					if (rev == YMODEM_ORDER_SOH) {
						g_ymodem_ctl.tar_header = YMODEM_ORDER_SOH;
						g_ymodem_ctl.ymodem_data_len = YMODEM_DATA_LEN_128;

					} else if (rev == YMODEM_ORDER_STX) {
						g_ymodem_ctl.tar_header = YMODEM_ORDER_STX;
						g_ymodem_ctl.ymodem_data_len = YMODEM_DATA_LEN_1024;

					} else if (rev == 0) {
						delay_ms(1);
					} else {
						y_debug_out("ymodem_recv_first_frame error\r\n");
						return REV_ERR;
					}


					rev = ymodem_dmessage_first_frame(g_ymodem_ctl.recv_buf, Rx_Max, g_ymodem_ctl.filename, g_ymodem_ctl.ymodem_data_len);
					if (rev != REV_OK) {
						y_debug_out("ymodem_dmessage_first_frame error\r\n");
						return REV_ERR;
					}

					g_ymodem_ctl.ymodel_status = YMODEM_RECV_DATA;
					send_usart_order(YMODEM_ORDER_ACK);
					break;
				}

			case YMODEM_RECV_DATA: {
					rev = ymodem_recv_data_frame(g_ymodem_ctl.recv_buf, Rx_Max, YMODEM_RECV_TIMEOUT, g_ymodem_ctl.ymodem_data_len);
					if (rev == g_ymodem_ctl.tar_header) {
						rev = strong_recv_data(callback, start_address, g_ymodem_ctl.recv_buf+3, g_ymodem_ctl.ymodem_data_len);
						if (rev != REV_OK) {
							while (1) {
								y_debug_out("ymodem recv data error\r\n");
							}
							y_debug_out("ymodem recv data error\r\n");
							return REV_ERR;
						}
						delay_ms(1);
						send_usart_order(YMODEM_ORDER_ACK);

					} else if (rev ==YMODEM_ORDER_EOT) { //数据结束帧
						g_ymodem_ctl.ymodel_status = YMODEM_RECV_END;
						delay_ms(1);
						send_usart_order(YMODEM_ORDER_NAK);

					} else {
						send_usart_order(YMODEM_ORDER_CAN);
						while (1) {
							y_debug_out("ymodem recv data error???[%x]\r\n", g_ymodem_ctl.recv_buf[0]);
						}
						y_debug_out("ymodem recv data error\r\n");
						return REV_ERR;
					}

					break;
				}

			case YMODEM_RECV_END: {
					rev = ymodem_recv_end_frame(g_ymodem_ctl.recv_buf, Rx_Max, YMODEM_RECV_TIMEOUT, YMODEM_ORDER_EOT);
					if (rev != REV_OK) {
						y_debug_out("ymodem_recv_end_frame error\r\n");
						return REV_ERR;
					}

					send_usart_order(YMODEM_ORDER_ACK);
					delay_ms(1);
					if (recv_usart_data(g_ymodem_ctl.recv_buf, Rx_Max) == REV_OK) {
						// y_debug_out("ymodem_disconnect error[0x%02x]\r\n", g_ymodem_ctl.recv_buf[0]);
					}
					send_usart_order(YMODEM_ORDER_C1);
					g_ymodem_ctl.ymodel_status = YMODEM_RECV_DISCONNECT;
				}


			case YMODEM_RECV_DISCONNECT: {
					rev = ymodem_disconnect(g_ymodem_ctl.recv_buf, Rx_Max, YMODEM_RECV_TIMEOUT, g_ymodem_ctl.tar_header);
					if (rev != REV_OK) {
						y_debug_out("ymodem_disconnect error\r\n");
						return REV_ERR;
					}

					send_usart_order(YMODEM_ORDER_ACK);
					y_debug_out("ymodem recv successful\r\n");
					flag = 1;
					break;
				}

			default:
				break;
		}

		if (flag == 1) {
			break;
		}
	}

	delay_ms(2000);
	y_debug_out("get filename[%s]\r\n", g_ymodem_ctl.filename);

	// for (int i=0; i<g_ymodem_ctl.data_len; i++) {
	// 	y_debug_out("%02x  ", g_ymodem_ctl.data_buf[i]);
	// }
	// y_debug_out("\r\n");

	return REV_OK;
}

